package nachos.network;

import nachos.machine.Lib;
import nachos.machine.MalformedPacketException;
import nachos.machine.Packet;

public class EnhancedPacket {

	
	/**
     * Allocate a new mail message to be sent, using the specified parameters.
     *
     * @param	dstLink		the destination link address.
     * @param	dstPort		the destination port.
     * @param	srcLink		the source link address.
     * @param	srcPort		the source port.
     * @param	contents	the contents of the packet.
     */
    public EnhancedPacket(int dstLink, int dstPort, int srcLink, int srcPort,
		       byte[] contents) throws MalformedPacketException {
	// make sure the paramters are valid
	if (dstPort < 0 || dstPort >= portLimit ||
	    srcPort < 0 || srcPort >= portLimit ||
	    contents.length > maxContentsLength)
	    throw new MalformedPacketException();

	this.dstPort = (byte) dstPort;
	this.srcPort = (byte) srcPort;
	this.contents = contents;

	byte[] packetContents = new byte[headerLength + contents.length];

	packetContents[0] = (byte) dstPort;
	packetContents[1] = (byte) srcPort;

	System.arraycopy(contents, 0, packetContents, headerLength,
			 contents.length);

	packet = new Packet(dstLink, srcLink, packetContents);
    }
    
    public EnhancedPacket(int dstLink, int dstPort, int srcLink, int srcPort,
    		boolean fin, boolean stp, boolean ack, boolean syn, int packetID,
    		byte[] contents) throws MalformedPacketException {
	// make sure the paramters are valid
	if (dstPort < 0 || dstPort >= portLimit ||
	    srcPort < 0 || srcPort >= portLimit ||
	    contents.length > maxContentsLength)
	    throw new MalformedPacketException();

	this.dstPort = (byte) dstPort;
	this.srcPort = (byte) srcPort;
	this.signal = (byte)findSignal(syn,ack,stp,fin);
	this.packetID = packetID;
	this.contents = contents;

	byte[] packetContents = new byte[headerLength + contents.length];

	packetContents[0] = (byte) dstPort;
	packetContents[1] = (byte) srcPort;
	packetContents[2] = (byte) 0;
	packetContents[3] = (byte)findSignal(syn,ack,stp,fin);
	System.arraycopy(Lib.bytesFromInt(this.packetID), 0 ,packetContents, 4,4);
	System.arraycopy(contents, 0, packetContents, headerLength,
			 contents.length);
	packet = new Packet(dstLink, srcLink, packetContents);
    }
	
    //copies information from a packet and
    public EnhancedPacket(Packet packet) throws MalformedPacketException {
	this.packet = packet;
	
	// make sure we have a valid header
	if (packet.contents.length < headerLength ||
	    packet.contents[0] < 0 || packet.contents[0] >= portLimit ||
	    packet.contents[1] < 0 || packet.contents[1] >= portLimit)
	    throw new MalformedPacketException();

	dstPort = packet.contents[0];
	srcPort = packet.contents[1];
	signal 	= (int)packet.contents[3];
	
	System.arraycopy(packet.contents, 4,Lib.bytesFromInt(this.packetID), 0, 4);  //saves the packet ID

	contents = new byte[packet.contents.length - headerLength];
	System.arraycopy(packet.contents, headerLength, contents, 0,
			 contents.length);
    }

    public boolean isEqual(EnhancedPacket differentPacket){
    	
    	if(packet.srcLink == differentPacket.packet.srcLink && (srcPort == differentPacket.srcPort) 
    			&& (packet.dstLink == differentPacket.packet.dstLink)
    			&& (packetID == differentPacket.packetID)){
    		return true;
    	}
    	else
    		return false;
    }
    
    
	public Packet convertToPacket() throws MalformedPacketException{
		byte[] tempContents = null;
		if( this.contents == null)
			tempContents = new byte[headerLength];
		else
			contents = new byte[headerLength + this.contents.length];
		tempContents[0] = (byte)this.dstPort;
		tempContents[1] = (byte)this.srcPort;
		tempContents[2] = 0;
		tempContents[3] = (byte)this.signal;
		System.arraycopy(Lib.bytesFromInt(this.packetID), 0, tempContents, 4, 4);
		if( this.contents != null )
			System.arraycopy(this.contents, 0, tempContents, headerLength, contents.length);
		return new Packet(this.dstLink, this.srcLink, tempContents);
	}
    

	
    /**
     * Return a string representation of the message headers.
     */
    public String toString() {
	return "from (" + packet.srcLink + ":" + srcPort +
	    ") to (" + packet.dstLink + ":" + dstPort +
	    "), " + contents.length + " bytes";
    }
    
    /** This message, as a packet that can be sent through a network link. */
    public Packet packet;
    /** The port used by this message on the destination machine. */
    public int dstPort;
    /** The port used by this message on the source machine. */
    public int srcPort;
    /** The contents of this message, excluding the mail message header. */
    public byte[] contents;
    public int signal;
    public int packetID; //each packet should have it's own unique packet ID
	public int dstLink;
    public int srcLink;
    
    //gets what kind of packet it is
    public static int findSignal(boolean syn, boolean ack, boolean stp, boolean fin){
    	int value = 0;
		if(fin)
			value += 0x08;
		if(stp)
			value += 0x04;
		if(ack)
			value += 0x02;
		if(syn)
			value += 0x01;
    	/*
    	if(syn)
    		value+= packetType.SYN.id;
    	if(ack)
    		value+= packetType.ACK.id;
    	if(stp)
    		value+= packetType.STP.id;
    	if(fin)
    		value+= packetType.FIN.id;
    	r*/
    	return value;
    }
    
    enum packetType{
    	DATA(0),SYN(1),ACK(2),SYNACT(3),STP(4),FIN(8),FINACK(10);
		private final int id;
	    packetType(int id) { this.id = (byte)id; }
	    public int getValue() { return id; }
	    public int getIntValue(){return (int)id;}
    }
    
    
    
    /**
     * The number of bytes in a mail header. The header is formatted as
     * follows:
     *
     * <table>
     * <tr><td>offset</td><td>size</td><td>value</td></tr>
     * <tr><td>0</td><td>1</td><td>destination port</td></tr>
     * <tr><td>1</td><td>1</td><td>source port</td></tr>
     * </table>
     */
    public static final int headerLength = 8;

    /** Maximum payload (real data) that can be included in a single mesage. */
    public static final int maxContentsLength =
	Packet.maxContentsLength - headerLength;

    /**
     * The upper limit on mail ports. All ports fall between <tt>0</tt> and
     * <tt>portLimit - 1</tt>.
     */    
    public static final int portLimit = 128;
}
